﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Forms;


namespace TextGenerator
{
    public partial class MainForm : Form
    {
        /// <summary>
        /// Indicates whether an asynchronous operation's 
        /// work has finished.
        /// </summary>
        private bool asyncWorkIsCompleted = true;

        /// <summary>
        /// Indicates that the user wants to close the form.
        /// </summary>
        /// <remarks>
        /// This is used to postpone the closing event until 
        /// the BackgroundWorker has been allowed to finish it's 
        /// cancel event (if any asynchronous work is running).
        /// </remarks>
        private bool formClosePending = false;


        #region Asynchronous Working Methods.
        /// <summary>
        /// Initializes and commences the generation of permutations.
        /// </summary>
        /// <param name="pattern">The pattern used to generate the permutations.</param>
        /// <param name="worker">The <see cref="BackgroundWorker"/> that handles the threading.</param>
        /// <param name="e">The event arguments used by the <see cref="BackgroundWorker"/>.</param>
        /// <returns>
        /// A <see cref="String"/> with the generated permutations.
        /// </returns>
        private string Generate(string pattern, BackgroundWorker worker, DoWorkEventArgs e)
        {
            // Initialize the permutator with the current pattern.
            try
            {
                // Try to initialize the permutator with the specified pattern.
                this.permutator.Initialize(worker, e, pattern);
            }
            catch (ArgumentException)
            {
                // Display an error message.
                MessageBox.Show(ERROR_INVALID_REFERENCES_TEXT, ERROR_INVALID_REFERENCES_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);

                // Cancel the generation.
                if (e != null)
                    e.Cancel = true;

                // Return an empty string as the result.
                return String.Empty;
            }
            catch (OverflowException)
            {
//TODO: This situation could be avoided by a pattern validation.
                // Display an error message.
                MessageBox.Show(ERROR_TOO_MANY_COMBINATIONS_TEXT, ERROR_TOO_MANY_COMBINATIONS_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);

                // Cancel the generation.
                if (e != null)
                    e.Cancel = true;

                // Return an empty string as the result.
                return String.Empty;
            }

            // Perform the generation.
            try
            {
                this.permutator.Generate();
            }
            catch (OutOfMemoryException)
            {
                // Display an error message.
                MessageBox.Show(ERROR_OUT_OF_MEMORY_TEXT, ERROR_OUT_OF_MEMORY_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);

                // Cancel the generation.
                if (e != null)
                    e.Cancel = true;

                // Return an empty string as the result.
                return String.Empty;
            }

            // Concatenate the generated list of permutations 
            // to a single string that can be displayed in 
            // the rich text box.
            List<string> generated = this.permutator.Permutations;
            return String.Concat(generated.ToArray());
        }

        // This event handler is where the actual,
        // potentially time-consuming work is done.
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            // Get the BackgroundWorker that raised this event.
            BackgroundWorker worker = sender as BackgroundWorker;

            // Assign the result of the computation
            // to the Result property of the DoWorkEventArgs
            // object. This is will be available to the 
            // RunWorkerCompleted eventhandler.
            e.Result = Generate((string)e.Argument, worker, e);
        }

        // This event handler updates the progress bar.
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.toolStripProgressBar1.Value = e.ProgressPercentage;
        }

        // This event handler deals with the results of the
        // background operation.
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // First, handle the case where an exception was thrown.
            if (e.Error != null)
            {
                MessageBox.Show(String.Format("{0}:{1}{2}", ERROR_ASYNC_TEXT, Environment.NewLine, e.Error.Message), ERROR_ASYNC_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);

                // Display the results.
                this.rtbResults.Text = e.Result.ToString();

                EndGeneration();
            }
            else if (e.Cancelled)
            {
                // Next, handle the case where the user canceled 
                // the operation.
                // Note that due to a race condition in 
                // the DoWork event handler, the Cancelled
                // flag may not have been set, even though
                // CancelAsync was called.
                Debug.WriteLine("Generation was cancelled.");

                // Update the visual elements.
                CancelGeneration();
            }
            else
            {
                // Finally, handle the case where the operation 
                // succeeded. 

                // Display the results.
                this.rtbResults.Text = e.Result.ToString();

                // Update the visual elements.
                EndGeneration();
            }

            // Change the flag to indicate that the asynchronous 
            // operation has finished.
            this.asyncWorkIsCompleted = true;

            // If a form closing is pending, 
            // execute it.
            if (this.formClosePending)
                this.Close();
        }
        #endregion Asynchronous Working Methods.
    }
}